#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <string.h>
#include <gtk/gtk.h>

#define BUFNUM 10    //缓冲区个数
#define BUFSIZE 1024 //一个缓冲区大小

int shm_id;     //共享内存id
int sem_id;     //信号量id
int pid1, pid2; //子进程1、2
union arg {
    int val;
} sem_num;

typedef struct _myBuf
{
    int in, out, length[BUFNUM];
    char buf[BUFNUM][BUFSIZE];
} myBuf;

//P操作
void P(int sem_id, int index)
{
    struct sembuf mybuf;
    mybuf.sem_num = index;
    mybuf.sem_op = -1;
    mybuf.sem_flg = 0;
    semop(sem_id, &mybuf, 1);
    return;
}

//V操作
void V(int sem_id, int index)
{
    struct sembuf mybuf;
    mybuf.sem_num = index;
    mybuf.sem_op = 1;
    mybuf.sem_flg = 0;
    semop(sem_id, &mybuf, 1);
}

void send_file_path(GtkWidget *widget, gpointer file_path)
{
    const gchar *file_path_text;
    //获取文本内容
    file_path_text = gtk_entry_get_text(GTK_ENTRY(file_path));
    printf("%s  length:%ld\n", file_path_text, strlen(file_path_text));

    int fifo_pipe;
    if ((fifo_pipe = open("fifo1", O_WRONLY)) < 0) //创建read的fifo文件
    {
        perror("Open FIFO failed");
        exit(-1);
    }
    if (write(fifo_pipe, file_path_text, strlen(file_path_text) + 1) < 0) //向fifo管道写入数据
    {
        perror("Write FIFO error");
        exit(-1);
    }
    close(fifo_pipe);
    if ((fifo_pipe = open("fifo2", O_WRONLY)) < 0) //创建write的fifo文件
    {
        perror("Open FIFO failed");
        exit(-1);
    }
    if (write(fifo_pipe, file_path_text, strlen(file_path_text) + 1) < 0) //向fifo管道写入数据
    {
        perror("Write FIFO error");
        exit(-1);
    }
    close(fifo_pipe);
}

gint quit()
{
    kill(pid1, SIGKILL); //杀死子进程1
    kill(pid2, SIGKILL); //杀死子进程2
    gtk_main_quit();
    return TRUE;
}

gint set_button_sensitive(gpointer button)
{
    gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
    return TRUE;
}

int main(int argc, char *argv[])
{
    key_t key = 233;
    //创建共享内存
    shm_id = shmget(key, BUFSIZE * BUFNUM + (2 + BUFNUM) * sizeof(int), IPC_CREAT | 0666);
    if (shm_id == -1)
    {
        perror("Shmget Error");
        exit(-1);
    }
    //创建2个信号灯
    sem_id = semget(key, 2, IPC_CREAT | 0666);
    if (sem_id == -1)
    {
        perror("Semget() Error");
        exit(-1);
    }
    sem_num.val = BUFNUM;
    semctl(sem_id, 0, SETVAL, sem_num); //对0号信号灯赋初值 1，表示共享缓冲区可以写入数据
    sem_num.val = 0;
    semctl(sem_id, 1, SETVAL, sem_num); //对1号信号灯赋初值 0，表示共享缓冲区可以读取数据

    if ((pid2 = fork()) == 0) //子进程 2
    {
        execv("./read", NULL);
    }
    else
    {
        if ((pid1 = fork()) == 0) //子进程 1
        {
            execv("./write", NULL);
        }
        else //父进程
        {
            printf("开始\n");
            GtkWidget *window;
            GtkWidget *button;
            GtkWidget *vbox, *label;
            gtk_init(&argc, &argv);                                                //初始化
            window = gtk_window_new(GTK_WINDOW_TOPLEVEL);                          //创建窗口
            gtk_widget_set_uposition(GTK_WIDGET(window), 360, 60);                 //设置窗口位置
            g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(quit), NULL); //监听窗口destroy事件
            gtk_window_set_title(GTK_WINDOW(window), "Main progress");             //设置窗口标题
            gtk_container_set_border_width(GTK_CONTAINER(window), 20);             //设置窗口宽度

            vbox = gtk_vbox_new(FALSE, 10);                           //创建纵向组件盒
            gtk_container_set_border_width(GTK_CONTAINER(vbox), 100); //设置组件盒宽度
            gtk_container_add(GTK_CONTAINER(window), vbox);           //将组件盒放入窗口
            gtk_widget_show(vbox);                                    //显示组件盒

            char temp_label[1024];
            sprintf(temp_label, "PID: %d", getpid());                   //组装标签
            label = gtk_label_new(temp_label);                          //生成标签
            gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 10); //将标签放入组件盒
            gtk_widget_show(label);                                     //显示label

            sprintf(temp_label, "Read's PID: %d", pid2);                //组装标签
            label = gtk_label_new(temp_label);                          //生成标签
            gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 10); //将标签放入组件盒
            gtk_widget_show(label);                                     //显示label

            sprintf(temp_label, "Write's PID: %d", pid1);               //组装标签
            label = gtk_label_new(temp_label);                          //生成标签
            gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 10); //将标签放入组件盒
            gtk_widget_show(label);                                     //显示label

            GtkWidget *hbox, *file_path;
            hbox = gtk_hbox_new(FALSE, 0);                             //创建横向组件盒
            gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);   //设置组件盒宽度
            gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 10); //将横向组件盒放入纵向组件盒
            gtk_widget_show(hbox);                                     //显示组件盒

            label = gtk_label_new("file path:");
            gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10); //将标签放入横向组件盒
            gtk_widget_show(label);                                     //显示label

            file_path = gtk_entry_new();                                    //创建file_path控件
            gtk_entry_set_max_length(GTK_ENTRY(file_path), 100);            //设置最大字符长度
            gtk_entry_set_text(GTK_ENTRY(file_path), "");                   //设置entry控件缺省值
            gtk_box_pack_start(GTK_BOX(hbox), file_path, FALSE, FALSE, 10); //将file_path放入组件盒
            gtk_widget_show(file_path);                                     //显示file_path

            button = gtk_button_new_with_label("copy");                  //创建带有标签的按钮
            gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 10); //将按钮加入到组件盒

            g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(send_file_path), file_path);    //获取输入的文件路径
            g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(set_button_sensitive), button); //按钮不使能

            gtk_widget_show(button); //显示button
            gtk_widget_show(window); //显示window
            gtk_main();

            wait(NULL);
            wait(NULL);
            remove("fifo1");             //删除管道文件
            remove("fifo2");             //删除管道文件
            shmctl(shm_id, IPC_RMID, 0); //释放共享内存
            semctl(sem_id, 0, IPC_RMID); //释放信号量集
            printf("结束\n");
        }
    }
    return 0;
}